#ifndef MEMUTIL_H
#define MEMUTIL_H


#include <windows.h>
namespace xy {
class Exception final {
    LPCTSTR msg;
public:
    Exception( LPCTSTR msg )
    {
        this->msg = msg;
    }


    LPCTSTR what()
    {
        return(this->msg);
    }
}
;



class File final{
    LPCTSTR file_name	= NULL;
    HANDLE	file_handle	= INVALID_HANDLE_VALUE;
public:
    File( LPCTSTR file_name)
    {
        this->file_name		= file_name;
        this->file_handle	= CreateFile( this->file_name,
                                       GENERIC_ALL,
                                       0,
                                       NULL,
                                       OPEN_ALWAYS,
                                       FILE_ATTRIBUTE_NORMAL,
                                       NULL
                                       );
        if ( this->file_handle == INVALID_HANDLE_VALUE )
        {
            throw Exception( TEXT( "打开文件失败" ) );
        }
    }

    int read(LPVOID lpBuffer,DWORD nNumberOfBytesToRead){
        DWORD NumberOfBytesRead = 0;
        if(!ReadFile(this->file_handle,lpBuffer,nNumberOfBytesToRead,&NumberOfBytesRead,NULL)){
            throw Exception(TEXT("读取文件失败!"));
        }
        return NumberOfBytesRead;
    }

    int write(LPVOID lpBuffer,DWORD nNumberOfBytesToWrite){
        DWORD NumberOfBytesWritten = 0;
        if(!WriteFile(this->file_handle,lpBuffer,nNumberOfBytesToWrite,&NumberOfBytesWritten,NULL)){
            throw Exception(TEXT("写入文件失败!"));
        }
        return NumberOfBytesWritten;
    }

    LONGLONG size(){
        LARGE_INTEGER s;
        if(!GetFileSizeEx(file_handle,&s)){
            throw Exception(TEXT("获取文件大小失败!"));
        }

        return s.QuadPart;
    }
    //
    DWORD64 set_pointer_pos(DWORD64 x,DWORD anchorPoint){
        DWORD64 pos = 0;
        LONG high = x>>32;
        DWORD low = SetFilePointer(this->file_handle,x,&high,anchorPoint);
        pos = high;
        pos <<= 32;
        pos += low;
        return pos;
    }

    DWORD64 rewind(){
        return pointer_from_begin(0);
    }

    DWORD64 pointer_from_begin(DWORD64 x){
        return set_pointer_pos(x,FILE_BEGIN);
    }

    DWORD64 pointer_from_end(DWORD64 x){
        return set_pointer_pos(x,FILE_END);
    }

    DWORD64 pointer_from_current(DWORD64 x){
        return set_pointer_pos(x,FILE_CURRENT);
    }


    LPCTSTR get_file_name()
    {
        return(this->file_name);
    }


    HANDLE get_handle()
    {
        return(this->file_handle);
    }


    void close_handle()
    {
        CloseHandle( this->file_handle );
        this->file_handle = INVALID_HANDLE_VALUE;
    }


    virtual ~File()
    {
        if ( this->file_handle != INVALID_HANDLE_VALUE )
        {
            this->close_handle();
        }
    }
}
;
/* template<class T> */
class FileMapMemory final {
    HANDLE	map_handle	= NULL;
    File	*file		= NULL;
    DWORD64 size		= 0;
    DWORD64 offset		= 0;
    LPVOID	view		= NULL;
    LPCTSTR shared_name	= NULL;

public:
    //    MapMemory( MapMemory & )	= delete;
    //    MapMemory( MapMemory && )	= delete;
    //    /* Mapping operator =(Mapping&)=delete; */
    //    MapMemory & operator =( MapMemory & ) = delete;


    FileMapMemory( bool exist, LPCTSTR shared_name, DWORD64 offset = 0, DWORD64 size = 0, File * file = NULL )
    {
        this->shared_name	= shared_name;
        this->offset		= offset;
        this->size		= size;
        this->file		= file;
        if ( exist )
        {
            this->map_handle = OpenFileMapping( FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE,
                                               FALSE,
                                               this->shared_name
                                               );
        } else {
            HANDLE map0_handle = INVALID_HANDLE_VALUE;
            if ( this->file )
            {
                map0_handle = this->file->get_handle();
            }
            this->map_handle = CreateFileMapping( map0_handle,
                                                 NULL,
                                                 PAGE_EXECUTE_READWRITE,
                                                 this->size >> 32,
                                                 (DWORD) this->size,
                                                 reinterpret_cast<LPCTSTR>(this->shared_name)
                                                 );
        }
        if ( !this->map_handle )
        {
            throw Exception( TEXT( "创建映射失败!" ) );
        }
        this->view = MapViewOfFile( this->map_handle,
                                   FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE,
                                   this->offset >> 32,
                                   (DWORD) this->offset,
                                   this->size
                                   );
        if ( !this->view )
        {
            throw Exception( TEXT( "创建映射视图失败!" ) );
        }
    }


    /*LPVOID data(){
         * return this->view;
         * }*/
    template<class T>
    T data()
    {
        return(static_cast<T>(this->view) );
    }


    HANDLE get_handle()
    {
        return(this->map_handle);
    }


    void close_handle()
    {
        CloseHandle( this->map_handle );
        this->map_handle = NULL;
    }


    void close_view()
    {
        UnmapViewOfFile( this->view );
        this->view = NULL;
    }


    virtual ~FileMapMemory()
    {
        if ( this->view )
        {
            close_view();
        }
        if ( this->map_handle )
        {
            close_handle();
        }
    }
}
;
class OtherProcess final{
    HANDLE	process_handle	= NULL;
    DWORD	processId	= -1;
public:


    OtherProcess( LPTSTR lpCommandLine )
    {
        STARTUPINFO		si;
        PROCESS_INFORMATION	pi;

        ZeroMemory( &si, sizeof(si) );
        si.cb = sizeof(si);
        ZeroMemory( &pi, sizeof(pi) );


        if ( !CreateProcess( NULL,
                           lpCommandLine,
                           NULL,
                           NULL,
                           FALSE,
                           0,
                           NULL,
                           NULL,
                           &si,
                           &pi
                           ) )
        {
            throw Exception( TEXT( "创建进程失败!" ) );
        }
        this->processId		= pi.dwProcessId;
        this->process_handle	= pi.hProcess;
    }


    OtherProcess( DWORD processId )
    {
        this->processId		= processId;
        this->process_handle	= OpenProcess( PROCESS_ALL_ACCESS, FALSE, this->processId );
        if ( !this->process_handle )
        {
            throw Exception( TEXT( "打开进程失败!" ) );
        }
    }


    DWORD get_process_id()
    {
        return(this->processId);
    }


    HANDLE get_handle()
    {
        return(process_handle);
    }


    void close_handle()
    {
        CloseHandle( process_handle );
        process_handle = NULL;
    }


    virtual ~OtherProcess()
    {
        if ( process_handle )
        {
            close_handle();
        }
    }
}
;
class OtherProcessMemory final {
    OtherProcess *process = NULL;

public:


    OtherProcessMemory( OtherProcess * p )
    {
        this->process = p;
    }

    LPVOID alloc( SIZE_T size )
    {
        LPVOID allocAddress = VirtualAllocEx( this->process->get_handle(),
                                             NULL,
                                             size,
                                             MEM_COMMIT,
                                             PAGE_EXECUTE_READWRITE
                                             );
        if ( !allocAddress )
        {
            throw Exception( TEXT( "给进程分配内存失败!" ) );
        }



        return(allocAddress);
    }

    void free(LPVOID address){
        if(!VirtualFreeEx(this->process->get_handle(), address, 0, MEM_RELEASE)){
            throw Exception(TEXT("释放内存失败!"));
        }
    }


    SIZE_T write( LPVOID address, LPVOID buffer, SIZE_T size )
    {
        SIZE_T writtenNum = 0;
        if ( !WriteProcessMemory( this->process->get_handle(),
                                address,
                                buffer,
                                size,
                                &writtenNum
                                ) )
        {
            throw Exception( TEXT( "写入内存数据失败!" ) );
        }
        return(writtenNum);
    }


    SIZE_T read( LPCVOID address, LPVOID buffer, SIZE_T size )
    {
        SIZE_T readNum = 0;
        if ( !ReadProcessMemory( this->process->get_handle(),
                               address,
                               buffer,
                               size,
                               &readNum ) )
        {
            throw Exception( TEXT( "读取内存数据失败!" ) );
        }
        return(readNum);
    }


}
;
class RemoteThread final {
    OtherProcess		*process	= NULL;
    LPTHREAD_START_ROUTINE	start_address	= NULL;
    HANDLE			thread_handle	= NULL;
    LPVOID			param		= NULL;
public:
    RemoteThread( OtherProcess * p, LPTHREAD_START_ROUTINE start_address, LPVOID param=NULL )
    {
        this->process		= p;
        this->start_address	= start_address;
        this->param		= param;
    }
    DWORD start()
    {
        DWORD threadId = -1;
        this->thread_handle = CreateRemoteThread( this->process->get_handle(),
                                                 NULL,
                                                 0,
                                                 start_address,
                                                 param,
                                                 0,
                                                 &threadId
                                                 );
        if ( !this->thread_handle )
        {
            throw Exception( TEXT( "创建进程线程失败!" ) );
        }
        return(threadId);
    }


    DWORD wait( DWORD dwMilliseconds = INFINITE )
    {
        return(WaitForSingleObject( this->thread_handle, dwMilliseconds ) );
    }


    virtual ~RemoteThread()
    {
        if ( this->thread_handle )
        {
            CloseHandle( this->thread_handle );
        }
    }
}
;
}



#endif // MEMUTIL_H
